package com.kun.dynamic.aop;

import cn.hutool.core.util.StrUtil;
import com.kun.dynamic.core.DynamicDsHolder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author kun.li
 * @annotation(Ds) 处理方法注解
 * @within(Ds) 处理类上注解
 */
@Aspect
@Slf4j
public class DynamicDsAspect {

    /**
     * 本地缓存 key:类的全类名+方法名称
     */
    private static final Map<String, String> cacheDsNameMap = new HashMap<>();

    @Pointcut("@annotation(Ds) || @within(Ds)")
    public void pointMethod() {
    }

    @Around(value = "pointMethod()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) {
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        Class<?> targetClass = proceedingJoinPoint.getTarget().getClass();
        Method targetMethod = signature.getMethod();

        String dsCacheKey = buildDsCacheKey(targetClass.getName(), targetMethod.getName());
        // 获取本次要执行的目标数据源
        String targetDsValue = cacheDsNameMap.get(dsCacheKey);
        if (StrUtil.isEmpty(targetDsValue)) {
            targetDsValue = foundTargetDsValue(targetClass, targetMethod);
        }
        DynamicDsHolder.pushDs(targetDsValue);
        cacheDsNameMap.put(dsCacheKey, targetDsValue);
        try {
            return proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            DynamicDsHolder.reset();
        }
    }

    private String buildDsCacheKey(String targetClassName, String targetMethodName) {
        return String.format("%s:%s", targetClassName, targetMethodName);
    }

    private String foundTargetDsValue(Class<?> targetClass, Method targetMethod) {
        // 获取目标类上注解
        Ds annotationInClass = AnnotationUtils.findAnnotation(targetClass, Ds.class);
        // 或者目标方法注解
        Ds annotationInMethod = AnnotationUtils.findAnnotation(targetMethod, Ds.class);
        // 目标方法上有注解 优先级大于目标类上的注解
        if (annotationInMethod != null) {
            return annotationInMethod.value();
        }
        return annotationInClass.value();
    }
}
